package com.touchwp.java.wechat.converter;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Named;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import com.touchwp.java.common.exception.SystemException;
import com.touchwp.java.common.log.Log;
import com.touchwp.java.wechat.message.LinkMessage;
import com.touchwp.java.wechat.message.LocationMessage;
import com.touchwp.java.wechat.message.Message;
import com.touchwp.java.wechat.message.MusicMessage;
import com.touchwp.java.wechat.message.NewsMessage;
import com.touchwp.java.wechat.message.PictureMessage;
import com.touchwp.java.wechat.message.TextMessage;

/**
 * 使用JAXB实现的消息转换器
 * 
 * @author yuki
 * 
 */
@Named
public class MessageConverter implements IMessageConverter {
	private Map<Class<?>, JAXBContext> jaxbCache = new HashMap<Class<?>, JAXBContext>();
	private ThreadLocal<Map<Class<?>, Marshaller>> MarshallerCache = new ThreadLocal<Map<Class<?>, Marshaller>>();
	private ThreadLocal<Map<Class<?>, Unmarshaller>> UnmarshallerCache = new ThreadLocal<Map<Class<?>, Unmarshaller>>();
	private NoCharacterEscapeHandler characterEscapeHandler = new NoCharacterEscapeHandler();
	private Class<?>[] suppotClasses = new Class<?>[] { NewsMessage.class, TextMessage.class, PictureMessage.class, LocationMessage.class, LinkMessage.class, MusicMessage.class };

	public MessageConverter() {
		try {
			for (Class<?> clazz : suppotClasses) {
				JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
				jaxbCache.put(clazz, jaxbContext);
			}
		} catch (Exception e) {
			throw new SystemException("JAXB消息转换器初始化失败", e);
		}
	}

	private Marshaller getMarshaller(Class<?> clazz) {
		Map<Class<?>, Marshaller> map = MarshallerCache.get();
		if (null == map) {
			map = new HashMap<Class<?>, Marshaller>(4);
			MarshallerCache.set(map);
		}
		Marshaller m = map.get(clazz);
		if (null == m) {
			JAXBContext context = jaxbCache.get(clazz);
			try {
				m = context.createMarshaller();
				m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
				m.setProperty("com.sun.xml.internal.bind.characterEscapeHandler", characterEscapeHandler);
			} catch (JAXBException e) {
				throw new SystemException("获取指定类的Marshaller失败 class为" + clazz.getName(), e);
			}
			MarshallerCache.get().put(clazz, m);
		}
		return m;
	}

	private Unmarshaller getUnmarshaller(Class<?> clazz) {

		Map<Class<?>, Unmarshaller> map = UnmarshallerCache.get();
		if (null == map) {
			map = new HashMap<Class<?>, Unmarshaller>(4);
			UnmarshallerCache.set(map);
		}

		Unmarshaller m = map.get(clazz);
		if (null == m) {
			JAXBContext context = jaxbCache.get(clazz);
			try {
				m = context.createUnmarshaller();
			} catch (JAXBException e) {
				throw new SystemException("获取指定类的Unmarshaller失败 class为" + clazz.getName(), e);
			}
			UnmarshallerCache.get().put(clazz, m);
		}
		return m;
	}

	@Override
	public String marshal(Message message) {
		StringWriter writer = null;
		try {
			Marshaller marshaller = getMarshaller(message.getClass());
			writer = new StringWriter();
			marshaller.marshal(message, writer);
		} catch (JAXBException e) {
			Log.error("转换为消息xml失败", e);
		} finally {
			try {
				if (null != writer) {
					writer.close();
				}
			} catch (IOException e) {
				Log.error(e);
			}
		}
		if (null != writer) {
			return writer.toString();
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T unmarshal(Class<T> clazz, String xml) {
		T message = null;
		StringReader reader = null;
		try {
			Unmarshaller um = getUnmarshaller(clazz);
			reader = new StringReader(xml);
			message = (T) um.unmarshal(reader);
		} catch (Exception e) {
			Log.error("转换为消息对象失败", e);
		} finally {
			if (null != reader) {
				reader.close();
			}
		}
		return message;
	}

}
